using System;
using System.Collections;
using System.Collections.Generic;
using FluentTests.Framework.Verbs;
using System.Linq;

namespace FluentTests.Framework.VerbExtensions
{
    public static class VerbCollectionExtensions
    {
        public static void Empty<T>(this Is<T> verb) where T : IEnumerable
        {

            VerbAssert<T>.Assert(
                verb,
                (actualValue) =>
                {
                    var hasElements = actualValue.GetEnumerator().MoveNext();
                    return hasElements;
                },                
                "Collection is not empty",
                "Collection is empty");

        }

        public static void AllOfSameType<T>(this Are<T> verb) where T : IEnumerable
        {
            VerbAssert<T>.Assert(
                verb,
                (actualValue) =>
                {
                    var distinctTypeCount = actualValue.Cast<object>().Select(a => a.GetType()).Distinct().Count();
                    return distinctTypeCount > 1;
                },
                "Assertion whether all items was of same type failed");
        }

        public static void MoreElementsThan<T>(this Has<T> verb, int compareValue) where T : IEnumerable
        {
            int actualCount = verb.EnsuredWrapperObject.ActualValue.Cast<object>().Count();
            VerbAssert<T>.Assert(
                verb,
                (actualValue) => actualCount <= compareValue,
                string.Format("Collection contains {0} items, expected more than {1}", actualCount, compareValue),
                string.Format("Collection contains {0} items, expected less or equal than {1}", actualCount, compareValue)
                );
        }
        public static void LessElementsThan<T>(this Has<T> verb, int compareValue) where T : IEnumerable
        {
            int actualCount = verb.EnsuredWrapperObject.ActualValue.Cast<object>().Count();
            VerbAssert<T>.Assert(
                verb,
                    (actualValue) => actualCount >= compareValue,
                string.Format("Collection contains {0} items, expected less than {1}", actualCount, compareValue),
                string.Format("Collection contains {0} items, expected more or equal than {1}", actualCount, compareValue)
                );
        }

        public static void ElementCount<T>(this Has<T> verb, int compareValue) where T : IEnumerable
        {
            int actualCount = verb.EnsuredWrapperObject.ActualValue.Cast<object>().Count();
            VerbAssert<T>.Assert(
                verb,(actualValue) => actualCount != compareValue,
                string.Format("Collection contains {0} items, expected {1} items", actualCount, compareValue),
                string.Format("Collection contains {0} items, did not expect {1} items", actualCount, compareValue));
        }

        public static void Elements<T>(this Has<T> verb) where T : IEnumerable
        {
            int actualCount = verb.EnsuredWrapperObject.ActualValue.Cast<object>().Count();
            VerbAssert<T>.Assert(
                verb, (actualValue) => actualCount == 0,
                string.Format("Collection contains no items, expected one or more items"),
                string.Format("Collection contains items, did not expect any items"));
        }

        public static void Any<T,TElement>(this GenericHas<T,TElement> verb, Func<TElement,bool> predicate ) where T : IEnumerable<TElement>
        {

            VerbAssert<T>.Assert(
                verb, (actualValue) => !actualValue.Any(predicate),
                "Collection contains no items",
                "Collection contains items");
        
        }

        public static void ValidForAll<TItem>(this Has<List<TItem>> verb, Func<TItem, bool> isValidFunc)
        {
            ValidForAllItems(verb.AssertionProvider, verb.EnsuredWrapperObject.ActualValue, isValidFunc);
        }

        public static void ValidForAll<TItem>(this Has<IList<TItem>> verb, Func<TItem, bool> isValidFunc)
        {
            ValidForAllItems(verb.AssertionProvider, verb.EnsuredWrapperObject.ActualValue, isValidFunc);
        }
        public static void ValidForAll<TItem>(this Has<IEnumerable<TItem>> verb, Func<TItem, bool> isValidFunc)
        {
            ValidForAllItems(verb.AssertionProvider, verb.EnsuredWrapperObject.ActualValue, isValidFunc);
        }

        private static void ValidForAllItems<TItem>(IAssertionProvider assertionProvider, IEnumerable<TItem> collection, Func<TItem,bool> isValidFunc) 
        {
            var q = collection.Where(a=> !isValidFunc(a));

            if (q.Count() > 0)
                assertionProvider.Fail("All items in the collection did not meet the requirements");
        }
       
    }
}